package se.gala.gw;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import se.gala.api.APICommand;
import se.gala.api.GCIGamesProtocol;
import se.gala.utils.GGObject;

final class GCIClientSocket extends GGObject {
    private PrintWriter out;
    private BufferedReader in;
    private Socket kkSocket;
    private boolean isServerStillOpen;
	
	public GCIClientSocket(String hostName, int portNumber, boolean useStdIn) {
        try (
//                final Socket kkSocket = new Socket(hostName, portNumber);
//                final PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
//                final BufferedReader in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
                final BufferedReader stdIn = new BufferedReader(new InputStreamReader(new TerminalInputStream()));
            ) {
        		kkSocket = new Socket(hostName, portNumber);
        		out = new PrintWriter(kkSocket.getOutputStream(), true);
            	in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
            	installServerListener();
            	if (useStdIn) {
	        		handleInputStream(stdIn);
	        		handleStream(in, stdIn, out);
            	}
            } catch (UnknownHostException e) {
                log("Don't know about host " + hostName, LogLevel.Error);
                System.exit(1);
            } catch (IOException e) {
                log("Couldn't get I/O for the connection to " +
                    hostName, LogLevel.Error);
                System.exit(1);
            }
	}
	
	public void installServerListener() throws IOException {
		Thread clientThread = new Thread() {
			public void run() {
				String fromServer = null;
				try {
					while (true) {
						if (kkSocket.isClosed()) {
							continue;
						}
						if ((fromServer = in.readLine()) != null) {
							isServerStillOpen = GCIClientSocket.handleServerResponse(fromServer);
							if (!isServerStillOpen) {
								break;
							}
						}
					}
				} catch (IOException e) {
					log(e.getMessage() + " when trying to install a server listener.", LogLevel.Error);
					e.printStackTrace();
				}
			}			
		};
		isServerStillOpen = true;
		clientThread.start();
	}
	
	public void sendRequest(String command) {
		if (isServerStillOpen) {
//			log("Sending request command: " + command);
			GCIClientSocket.sendCommand(command, out, false);
		} else {
			log("Server is no longer listening", LogLevel.Notice);
		}
	}
	
	private static void sendCommand(String command, PrintWriter out, boolean isResponse) {
        final APICommand clientCommand = GCIGamesProtocol.parseCommand(command);
        if (command != null) {
        	if (clientCommand.isResponse() != isResponse) {
        		log("Request/Response mismatch when sending command:\n" + clientCommand, LogLevel.Error);
        	} else {
	            log("Client: " + clientCommand);
        	}
            out.println(clientCommand.tokenize());
        }		
	}
	
	public static boolean handleServerResponse(String fromServer) {
    	if (fromServer != null) {
    		final APICommand serverCommand = GCIGamesProtocol.parseCommand(fromServer);
    		log("Server: " + serverCommand);
    		GWClient.serverResponseReceived(serverCommand);
            if (GCIGamesProtocol.isTerminateCommand(serverCommand)) {
                return false;
            }
    	}
    	return true;
	}
	
	private void handleInputStream(BufferedReader stdIn) throws IOException {
		do {
			/* the thread stops here and waits for input */
	    	final String fromUser = stdIn.readLine();
	    	sendRequest(fromUser);
		} while (isServerStillOpen);
	}
	
	private void handleStream(BufferedReader in, BufferedReader stdIn, PrintWriter out) throws IOException {
        String fromServer = null;
        do {
        	isServerStillOpen = handleServerResponse(fromServer);
        	if (!isServerStillOpen) {
        		break;
        	}
            /* the thread stops here and waits for input */
        	final String fromUser = stdIn.readLine();
        	sendCommand(fromUser, out, false);
        } while ((fromServer = in.readLine()) != null);
	}
}
